package com.jsy.seckill.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jsy.common.constant.SeckillConstant;
import com.jsy.common.util.IdWorker;
import com.jsy.common.util.RedisLock;
import com.jsy.seckill.dao.SeckillGoodsMapper;
import com.jsy.seckill.dao.SeckillOrderMapper;
import com.jsy.seckill.pojo.SeckillGoods;
import com.jsy.seckill.pojo.SeckillOrder;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/****
 * @Author:jsy
 * @Description:SeckillOrder业务层接口实现类
 * @Date goalong
 *****/
@Service
public class SeckillOrderServiceImpl implements SeckillOrderService {

    @Autowired
    private SeckillOrderMapper seckillOrderMapper;

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${mq.pay.queue.delay}")
    private String delayQueue;

    /**
     * SeckillOrder条件+分页查询
     *
     * @param seckillOrder 查询条件
     * @param page         页码
     * @param size         页大小
     * @return 分页结果
     */
    @Override
    public PageInfo<SeckillOrder> findPage(SeckillOrder seckillOrder, int page, int size) {
        //分页
        PageHelper.startPage(page, size);
        //搜索条件构建
        Example example = createExample(seckillOrder);
        //执行搜索
        return new PageInfo<SeckillOrder>(seckillOrderMapper.selectByExample(example));
    }

    /**
     * SeckillOrder分页查询
     *
     * @param page
     * @param size
     * @return
     */
    @Override
    public PageInfo<SeckillOrder> findPage(int page, int size) {
        //静态分页
        PageHelper.startPage(page, size);
        //分页查询
        return new PageInfo<SeckillOrder>(seckillOrderMapper.selectAll());
    }

    /**
     * SeckillOrder条件查询
     *
     * @param seckillOrder
     * @return
     */
    @Override
    public List<SeckillOrder> findList(SeckillOrder seckillOrder) {
        //构建查询条件
        Example example = createExample(seckillOrder);
        //根据构建的条件查询数据
        return seckillOrderMapper.selectByExample(example);
    }


    /**
     * SeckillOrder构建查询对象
     *
     * @param seckillOrder
     * @return
     */
    public Example createExample(SeckillOrder seckillOrder) {
        Example example = new Example(SeckillOrder.class);
        Example.Criteria criteria = example.createCriteria();
        if (seckillOrder != null) {
            // 主键
            if (!StringUtils.isEmpty(seckillOrder.getId())) {
                criteria.andEqualTo("id", seckillOrder.getId());
            }
            // 秒杀商品ID
            if (!StringUtils.isEmpty(seckillOrder.getSeckillId())) {
                criteria.andEqualTo("seckillId", seckillOrder.getSeckillId());
            }
            // 支付金额
            if (!StringUtils.isEmpty(seckillOrder.getMoney())) {
                criteria.andEqualTo("money", seckillOrder.getMoney());
            }
            // 用户
            if (!StringUtils.isEmpty(seckillOrder.getUserId())) {
                criteria.andEqualTo("userId", seckillOrder.getUserId());
            }
            // 创建时间
            if (!StringUtils.isEmpty(seckillOrder.getCreateTime())) {
                criteria.andEqualTo("createTime", seckillOrder.getCreateTime());
            }
            // 支付时间
            if (!StringUtils.isEmpty(seckillOrder.getPayTime())) {
                criteria.andEqualTo("payTime", seckillOrder.getPayTime());
            }
            // 状态，0未支付，1已支付
            if (!StringUtils.isEmpty(seckillOrder.getStatus())) {
                criteria.andEqualTo("status", seckillOrder.getStatus());
            }
            // 收货人地址
            if (!StringUtils.isEmpty(seckillOrder.getReceiverAddress())) {
                criteria.andEqualTo("receiverAddress", seckillOrder.getReceiverAddress());
            }
            // 收货人电话
            if (!StringUtils.isEmpty(seckillOrder.getReceiverMobile())) {
                criteria.andEqualTo("receiverMobile", seckillOrder.getReceiverMobile());
            }
            // 收货人
            if (!StringUtils.isEmpty(seckillOrder.getReceiver())) {
                criteria.andEqualTo("receiver", seckillOrder.getReceiver());
            }
            // 交易流水
            if (!StringUtils.isEmpty(seckillOrder.getTransactionId())) {
                criteria.andEqualTo("transactionId", seckillOrder.getTransactionId());
            }
        }
        return example;
    }

    /**
     * 删除
     *
     * @param id
     */
    @Override
    public Integer delete(Long id) {
        return seckillOrderMapper.deleteByPrimaryKey(id);
    }

    /**
     * 修改SeckillOrder
     *
     * @param seckillOrder
     */
    @Override
    public Integer update(SeckillOrder seckillOrder) {
        return seckillOrderMapper.updateByPrimaryKey(seckillOrder);
    }

    /**
     * 增加SeckillOrder
     *
     * @param seckillOrder
     */
    @Override
    public Integer add(SeckillOrder seckillOrder) {
        return seckillOrderMapper.insert(seckillOrder);
    }

    /**
     * 根据ID查询SeckillOrder
     *
     * @param id
     * @return
     */
    @Override
    public SeckillOrder findById(Long id) {
        return seckillOrderMapper.selectByPrimaryKey(id);
    }

    /**
     * 查询SeckillOrder全部数据
     *
     * @return
     */
    @Override
    public List<SeckillOrder> findAll() {
        return seckillOrderMapper.selectAll();
    }

    /***
     *秒杀下单
     * @return 查询结果
     */
    @Override
    public Long orderCreate(String username, String time, Long id) throws InterruptedException {
        RedisLock redisLock = new RedisLock(redisTemplate);
        if (redisLock.lock(id.toString())) {
            //1.查询秒杀品库存是否>0,是否秒完
            SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(SeckillConstant.SECKILL_GOODS_PREFIX + time).get(id);
            //判断秒杀商品是否存在
            if (seckillGoods != null) {
                //判断库存是否大于0
                if (seckillGoods.getStockCount() == 0) {
                    throw new RuntimeException("商品已经秒完！");
                }
                //是否已经抢购但未支付，防止重复排队(限制重复下单)
                Boolean member = redisTemplate.boundSetOps(SeckillConstant.USER_SECKILL_GOODS_PREFIX + id).isMember(username);
                if (member != null && member) {
                    throw new RuntimeException("同一个商品在支付之前，不能再次抢购");
                }

                //2.更新redis秒杀商品的库存
                seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);
                if (seckillGoods.getStockCount() > 0) {
                    redisTemplate.boundHashOps(SeckillConstant.SECKILL_GOODS_PREFIX + time).put(seckillGoods.getId(),seckillGoods);
                } else {
                    //4.如果为0,更新商品数据，删除缓存，
                    seckillGoodsMapper.updateByPrimaryKeySelective(seckillGoods);
                    redisTemplate.boundHashOps(SeckillConstant.SECKILL_GOODS_PREFIX + time).delete(seckillGoods.getId());
                }

                //释放分布式锁
                redisLock.unlock(id.toString());
                //3.秒杀下单（设置订单信息和状态）
                SeckillOrder seckillOrder = new SeckillOrder();
                //使用idWorker生成id
                seckillOrder.setId(idWorker.nextId());
                seckillOrder.setSeckillId(id);
                seckillOrder.setCreateTime(new Date());
                seckillOrder.setUserId(username);
                //使用秒杀价格
                seckillOrder.setMoney(seckillGoods.getCostPrice());
                //支付状态
                seckillOrder.setStatus("0");
                //5.保存订单到redis中
                redisTemplate.boundHashOps(SeckillConstant.SECKILL_ORDERS).put(seckillOrder.getId().toString(), seckillOrder);
                //创建重复排队标识
                redisTemplate.boundSetOps(SeckillConstant.USER_SECKILL_GOODS_PREFIX + id).add(username);
                //发送延时队列，限制支付时间(需要参数用户名，订单号，时间段)
                Map<String, String> map = new HashMap<>();
                map.put("outTradeNo",seckillOrder.getId().toString());
                map.put("time",time);
                map.put("username",username);
                rabbitTemplate.convertAndSend(delayQueue,map, new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        message.getMessageProperties().setExpiration("10000");
                        return message;
                    }
                });
                return seckillOrder.getId();
            }
        }
        return null;
    }

    /**
     * 支付成功保存秒杀订单到数据库中
     *
     * @param outTradeNo    秒杀订单id
     * @param transactionId 交易号
     * @param username      用户名
     */
    @Override
    public void saveOrderInRedisToDB(String outTradeNo, String transactionId, String username) {
        //支付成功 (先查询有没有该订单)更新redis订单支付状态 同步到mysql中 删除redis订单数据 删除排队标识
        //1更新秒杀订单状态
        SeckillOrder seckillOrder = findSeckillOrderInRedis(outTradeNo);
        if (seckillOrder != null) {
            seckillOrder.setStatus("1");
            seckillOrder.setPayTime(new Date());
            seckillOrder.setTransactionId(transactionId);
            //2同步到数据库
            seckillOrderMapper.insertSelective(seckillOrder);
            //3删除redis中订单（删除排队标识）
            deleteSeckillOrderInRedis(outTradeNo, username);
        }
    }

    /**
     * 根据订单号查在redis中的订单
     *
     * @param outTradeNo 订单号
     * @return 秒杀订单
     */
    @Override
    public SeckillOrder findSeckillOrderInRedis(String outTradeNo) {
        return (SeckillOrder) redisTemplate.boundHashOps(SeckillConstant.SECKILL_ORDERS).get(outTradeNo);
    }


    /**
     * 支付失败 主要删除redis中的订单
     * @param outTradeNo 秒杀订单id
     * @param time       秒杀时间段
     * @param username   用户名
     */
    @Override
    public void deleteOrder(String outTradeNo, String time, String username) {
        try {
            //支付失败 (先查询有没有该订单)回滚redis库存 删除redis订单 需要查看秒杀商品的time 商品id 用户名用来删除重复排队标识
            //1回滚库存(查看订单获取商品id)
            SeckillOrder seckillOrder = findSeckillOrderInRedis(outTradeNo);
            //添加分布式锁
            RedisLock redisLock = new RedisLock(redisTemplate);
            if (redisLock.lock(seckillOrder.getSeckillId().toString())) {
                //查看redis中库存状态(有可能正好没有库存)
                SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps(SeckillConstant.SECKILL_GOODS_PREFIX + time).get(seckillOrder.getSeckillId());
                if (seckillGoods == null) {
                    //从数据库中获取秒杀商品
                    seckillGoods = seckillGoodsMapper.selectByPrimaryKey(seckillOrder.getSeckillId());
                }
                //增加库存
                seckillGoods.setStockCount(seckillGoods.getStockCount() + 1);
                redisTemplate.boundHashOps(SeckillConstant.SECKILL_GOODS_PREFIX + time).put(seckillGoods.getId(), seckillGoods);
                //释放分布式锁
                redisLock.unlock(seckillGoods.getId().toString());
                //2删除redis订单
                deleteSeckillOrderInRedis(outTradeNo,username);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 删除redis中的订单
     *
     * @param outTradeNo 订单号
     * @param username   用户名
     */
    @Override
    public void deleteSeckillOrderInRedis(String outTradeNo, String username) {
        //获取秒杀订单中对应的秒杀商品id
        SeckillOrder seckillOrder = findSeckillOrderInRedis(outTradeNo);
        //删除redis中订单
        redisTemplate.boundHashOps(SeckillConstant.SECKILL_ORDERS).delete(outTradeNo);
        //删除重复排队标识
        redisTemplate.boundSetOps(SeckillConstant.USER_SECKILL_GOODS_PREFIX + seckillOrder.getSeckillId()).remove(username);
    }


}
